package com.liu.qqRobot.climbingTower.handler;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.BetweenFormatter;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.RandomUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.liu.common.content.RedisKeyInterface;
import com.liu.common.utils.HtmlToImageUtils;
import com.liu.common.utils.QQClientUtils;
import com.liu.mybatisPlus.climbingTower.entity.base.*;
import com.liu.mybatisPlus.climbingTower.service.*;
import com.liu.mybatisPlus.q.service.QGroupUserOperationQuantityService;
import com.liu.qqRobot.climbingTower.entity.dto.UserEquipAttributeDTO;
import com.liu.qqRobot.climbingTower.runnable.AttackRunnable;
import com.liu.qqRobot.event.entity.dto.EventReportingDTO;
import com.liu.qqRobot.q.enums.OperationTypeEnum;
import freemarker.template.TemplateException;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.xml.sax.SAXException;

import javax.xml.parsers.ParserConfigurationException;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;

@Slf4j
@Component
public class ClimbingTowerStartHandler {

    @Resource
    private ClimbingTowerUserInfoService climbingTowerUserInfoService;

    @Resource
    private ClimbingTowerEntityInfoService climbingTowerEntityInfoService;

    @Resource
    private ClimbingTowerEntityKillDropService climbingTowerEntityKillDropService;

    @Resource
    private ClimbingTowerEntityKillRewardService climbingTowerEntityKillRewardService;

    @Resource
    private RedisTemplate<String, String> redisTemplate;

    @Resource
    private QGroupUserOperationQuantityService qGroupUserOperationQuantityService;

    @Resource
    private ClimbingTowerArticleInfoService climbingTowerArticleInfoService;

    @Resource
    private ClimbingTowerUserArticleService climbingTowerUserArticleService;

    @Resource
    private ClimbingTowerUserEquipService climbingTowerUserEquipService;

    @Resource
    private ClimbingTowerArticleAttributesService climbingTowerArticleAttributesService;

    /**
     * 处理指令
     * @param data data
     * @param groupId groupId
     * @param userId userId
     * @param isGroup isGroup
     */
    public void handler(EventReportingDTO.Message.Data data, Long groupId, Long userId, Boolean isGroup) {
        // 判断玩家是否在失败恢复状态
        boolean userIsFail = this.judgeUserIsFail(groupId, userId, isGroup);
        if (userIsFail) {
            return;
        }
        List<String> msgList = Collections.synchronizedList(new ArrayList<>());
        // 判断玩家是否存在
        ClimbingTowerUserInfo climbingTowerUserInfo = this.judgeUserExistOrCreate(groupId, userId, msgList);
        // 获取怪物信息
        ClimbingTowerEntityInfo climbingTowerEntityInfo = this.getMonster();
        msgList.add("<div>遭遇野怪：【" + climbingTowerEntityInfo.getName() + "】</div>");
        UserEquipAttributeDTO userEquipAttributeDTO = climbingTowerUserEquipService.calcUserEquipAttribute(groupId, userId);
        AtomicInteger userHp = new AtomicInteger(climbingTowerUserInfo.getHp() + userEquipAttributeDTO.getHp());
        AtomicInteger userAggressivity = new AtomicInteger(climbingTowerUserInfo.getAggressivity() + userEquipAttributeDTO.getAggressivity());
        AtomicInteger userAgile = new AtomicInteger(climbingTowerUserInfo.getAgile() + userEquipAttributeDTO.getAgile());
        AtomicInteger monsterHp = new AtomicInteger(climbingTowerEntityInfo.getHp());
        AtomicInteger monsterAggressivity = new AtomicInteger(climbingTowerEntityInfo.getAggressivity());
        AtomicInteger monsterAgile = new AtomicInteger(climbingTowerEntityInfo.getAgile());
        do {
            CountDownLatch countDownLatch = new CountDownLatch(2);
            ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor((int) countDownLatch.getCount(),
                    (int) countDownLatch.getCount(), 10, TimeUnit.SECONDS,
                    new ArrayBlockingQueue<>(50), Executors.defaultThreadFactory(), new ThreadPoolExecutor.CallerRunsPolicy());
            threadPoolExecutor.submit(new AttackRunnable(countDownLatch, monsterAgile, userHp, monsterAggressivity,
                    climbingTowerEntityInfo.getName(), "用户", msgList));
            threadPoolExecutor.submit(new AttackRunnable(countDownLatch, userAgile, monsterHp, userAggressivity,
                    "用户", climbingTowerEntityInfo.getName(), msgList));
            try {
                countDownLatch.await();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            if (userAgile.get() >= 20) {
                threadPoolExecutor.shutdownNow();
                userAgile.set(userAgile.get() - 20);
            }
            if (monsterAgile.get() >= 20) {
                threadPoolExecutor.shutdownNow();
                monsterAgile.set(monsterAgile.get() - 20);
            }
            if (userHp.get() <= 0) {
                String failTime = DateUtil.now();
                String restoreTime = this.calcRestoreTime(failTime, climbingTowerUserInfo.getHp());
                redisTemplate.opsForValue().set(RedisKeyInterface.CLIMBING_TOWER_FAIL + groupId + userId, failTime);
                redisTemplate.opsForValue().set(RedisKeyInterface.CLIMBING_TOWER_RESTORE + groupId + userId, restoreTime);
                String s = DateUtil.formatBetween(DateUtil.parse(failTime), DateUtil.parse(restoreTime), BetweenFormatter.Level.SECOND);
                msgList.add("<div>被【" + climbingTowerEntityInfo.getName() + "】击败，返回城镇恢复，恢复时间：" + s + "</div>");
                threadPoolExecutor.shutdownNow();
                break;
            }
            if (monsterHp.get() <= 0) {
                msgList.add("<div style=\"color: #64ab6f\">用户击败【" + climbingTowerEntityInfo.getName() + "】</div>");
                this.getReward(climbingTowerUserInfo, climbingTowerEntityInfo.getId(), msgList);
                boolean haveDrop = this.judgeHaveDrop();
                if (haveDrop) {
                    this.getDrop(groupId, userId, climbingTowerEntityInfo.getId(), msgList);
                }
                threadPoolExecutor.shutdownNow();
                break;
            }
        } while (true);
        climbingTowerUserInfoService.updateById(climbingTowerUserInfo);
        Map<String, Object> templateParam = new HashMap<>();
        templateParam.put("msgList", msgList);
        try {
            File file = HtmlToImageUtils.htmlToImage("climbingTowerStartTemplate.html", templateParam,
                    "src/main/resources/picture/climbingTowerStart.png", 1500);
            String encode = Base64.encode(file);
            String msg = "[CQ:at,qq=" + userId + "]\n[CQ:image,file=base64://" + encode+ ",type=show,subType=0]";
            QQClientUtils.sendMsg(msg, userId, groupId, isGroup);
            qGroupUserOperationQuantityService.statisticsOperationQuantityByType(groupId, userId, OperationTypeEnum.CLIMBING_TOWER_START);
        } catch (IOException | ParserConfigurationException | SAXException | TemplateException e) {
            log.error("获取爬塔展示模板失败:{}", e.getMessage(), e);
        }
    }

    /**
     * 判断玩家是否还处于被击败状态（处于被击败状态的会发送Q群提醒）
     * @param groupId groupId
     * @param userId userId
     * @param isGroup isGroup
     * @return boolean
     */
    private boolean judgeUserIsFail(Long groupId, Long userId, Boolean isGroup) {
        boolean result = false;
        // 被击败时间
        String lastFailTimeStr = redisTemplate.opsForValue().get(RedisKeyInterface.CLIMBING_TOWER_FAIL + groupId + userId);
        if (lastFailTimeStr != null) {
            // 恢复完成时间
            String restoreTimeStr = redisTemplate.opsForValue().get(RedisKeyInterface.CLIMBING_TOWER_RESTORE + groupId + userId);
            DateTime restoreTime = DateUtil.parse(restoreTimeStr);
            if (DateUtil.date().before(restoreTime)) {
                String s = DateUtil.formatBetween(DateUtil.date(), restoreTime, BetweenFormatter.Level.SECOND);
                String msg = "[CQ:at,qq=" + userId + "]\n你在上一次爬塔时被击败了，还在恢复中，恢复剩余时间：" + s;
                QQClientUtils.sendMsg(msg, userId, groupId, isGroup);
                result = true;
            } else {
                redisTemplate.delete(RedisKeyInterface.CLIMBING_TOWER_FAIL + groupId + userId);
                redisTemplate.delete(RedisKeyInterface.CLIMBING_TOWER_RESTORE + groupId + userId);
            }
        }
        return result;
    }

    /**
     * 判断玩家是否存在，不存在则创建
     * @param groupId groupId
     * @param userId userId
     * @param msgList msgList
     */
    private ClimbingTowerUserInfo judgeUserExistOrCreate(Long groupId, Long userId, List<String> msgList) {
        QueryWrapper<ClimbingTowerUserInfo> userInfoQueryWrapper = new QueryWrapper<>();
        userInfoQueryWrapper.eq("group_id", groupId);
        userInfoQueryWrapper.eq("user_id", userId);
        ClimbingTowerUserInfo climbingTowerUserInfo = climbingTowerUserInfoService.getOne(userInfoQueryWrapper);
        if (climbingTowerUserInfo == null) {
            climbingTowerUserInfo = new ClimbingTowerUserInfo();
            climbingTowerUserInfo.setGroupId(groupId);
            climbingTowerUserInfo.setUserId(userId);
            climbingTowerUserInfo.setGrade(0);
            climbingTowerUserInfo.setHp(10);
            climbingTowerUserInfo.setAggressivity(5);
            climbingTowerUserInfo.setDrinkBlood(0);
            climbingTowerUserInfo.setSpecies(10);
            climbingTowerUserInfo.setExperience(0);
            climbingTowerUserInfoService.save(climbingTowerUserInfo);
            msgList.add("<div>创建角色，开始爬塔</div>");
        }
        return climbingTowerUserInfo;
    }

    /**
     * 获取怪物信息
     * @return ClimbingTowerEntityInfo
     */
    private ClimbingTowerEntityInfo getMonster() {
        QueryWrapper<ClimbingTowerEntityInfo> climbingTowerEntityInfoQueryWrapper = new QueryWrapper<>();
        climbingTowerEntityInfoQueryWrapper.eq("entity_type", "monster");
        List<ClimbingTowerEntityInfo> climbingTowerEntityInfoList = climbingTowerEntityInfoService.list(climbingTowerEntityInfoQueryWrapper);
        return climbingTowerEntityInfoList.get(RandomUtil.randomInt(climbingTowerEntityInfoList.size()));
    }

    /**
     * 根据玩家总血量计算恢复时间（60秒恢复5点）
     * @param failTime failTime
     * @param hp hp
     * @return DateTime
     */
    private String calcRestoreTime(String failTime, Integer hp) {
        BigDecimal restoreMinute = BigDecimal.valueOf(hp).divide(BigDecimal.valueOf(5), 0, RoundingMode.HALF_UP);
        return DateUtil.formatDateTime(DateUtil.offsetSecond(DateUtil.parseDateTime(failTime), restoreMinute.multiply(BigDecimal.valueOf(60)).intValue()));
    }

    /**
     * 获取怪物击败奖励
     * @param climbingTowerUserInfo climbingTowerUserInfo
     * @param climbingTowerEntityInfoId climbingTowerEntityInfoId
     * @param msgList msgList
     */
    private void getReward(ClimbingTowerUserInfo climbingTowerUserInfo, Integer climbingTowerEntityInfoId, List<String> msgList) {
        QueryWrapper<ClimbingTowerEntityKillReward> entityKillRewardQueryWrapper = new QueryWrapper<>();
        entityKillRewardQueryWrapper.eq("entity_id", climbingTowerEntityInfoId);
        List<ClimbingTowerEntityKillReward> entityKillRewardList = climbingTowerEntityKillRewardService.list(entityKillRewardQueryWrapper);
        for (ClimbingTowerEntityKillReward entityKillReward : entityKillRewardList) {
            switch (entityKillReward.getKey()) {
                case "experience" -> {
                    msgList.add("<div>获得奖励：经验值【" + entityKillReward.getValue() + "】</div>");
                    climbingTowerUserInfo.setExperience(climbingTowerUserInfo.getExperience() + Integer.parseInt(entityKillReward.getValue()));
                    // 计算升级
                    this.calcUpgrade(climbingTowerUserInfo, msgList);
                }
                case "species" -> {
                    msgList.add("<div>获得奖励：金币【" + entityKillReward.getValue() + "】</div>");
                    climbingTowerUserInfo.setSpecies(climbingTowerUserInfo.getSpecies() + Integer.parseInt(entityKillReward.getValue()));
                }
            }
        }
    }

    /**
     * 计算玩家升级
     * @param climbingTowerUserInfo climbingTowerUserInfo
     * @param msgList msgList
     */
    private void calcUpgrade(ClimbingTowerUserInfo climbingTowerUserInfo, List<String> msgList) {
        Integer oldGrade = climbingTowerUserInfo.getGrade();
        Integer oldHp = climbingTowerUserInfo.getHp();
        Integer oldAggressivity = climbingTowerUserInfo.getAggressivity();
        Integer oldAgile = climbingTowerUserInfo.getAgile();
        boolean isUpgrade = false;
        do {
            Integer grade = climbingTowerUserInfo.getGrade();
            Integer hp = climbingTowerUserInfo.getHp();
            Integer aggressivity = climbingTowerUserInfo.getAggressivity();
            Integer agile = climbingTowerUserInfo.getAgile();
            // 当前等级需要的经验
            Integer needEx = BigDecimal.valueOf(grade + 1).multiply(BigDecimal.valueOf(50)).multiply(BigDecimal.valueOf(1.2)).intValue();
            Integer experience = climbingTowerUserInfo.getExperience();
            if (needEx <= experience) {
                climbingTowerUserInfo.setExperience(experience - needEx);
                climbingTowerUserInfo.setGrade(grade + 1);
                climbingTowerUserInfo.setHp(BigDecimal.valueOf(hp).multiply(BigDecimal.valueOf(1.2)).intValue());
                climbingTowerUserInfo.setAggressivity(BigDecimal.valueOf(aggressivity).multiply(BigDecimal.valueOf(1.2)).intValue());
                climbingTowerUserInfo.setAgile(BigDecimal.valueOf(agile).multiply(BigDecimal.valueOf(1.2)).intValue());
                isUpgrade = true;
            } else {
                break;
            }
        } while (true);
        if (isUpgrade) {
            msgList.add("<div style=\"color: gold\">升级</div>");
            msgList.add("<div style=\"color: gold\">等级提升：" + oldGrade + "→" + climbingTowerUserInfo.getGrade() + "</div>");
            if (!oldHp.equals(climbingTowerUserInfo.getHp())) {
                msgList.add("<div style=\"color: gold\">血量提升：" + oldHp + "→" + climbingTowerUserInfo.getHp() + "</div>");
            }
            if (!oldAggressivity.equals(climbingTowerUserInfo.getAggressivity())) {
                msgList.add("<div style=\"color: gold\">攻击力提升：" + oldAggressivity + "→" + climbingTowerUserInfo.getAggressivity() + "</div>");
            }
            if (!oldAgile.equals(climbingTowerUserInfo.getAgile())) {
                msgList.add("<div style=\"color: gold\">敏捷提升：" + oldAgile + "→" + climbingTowerUserInfo.getAgile() + "</div>");
            }
        }
    }

    /**
     * 判断是否有掉落物
     * @return boolean
     */
    private boolean judgeHaveDrop() {
        return RandomUtil.randomInt(100) >= 90;
    }

    /**
     * 获取怪物掉落物
     * @param groupId groupId
     * @param userId userId
     * @param climbingTowerEntityInfoId climbingTowerEntityInfoId
     * @param msgList msgList
     */
    private void getDrop(Long groupId, Long userId, Integer climbingTowerEntityInfoId, List<String> msgList) {
        QueryWrapper<ClimbingTowerEntityKillDrop> entityKillDropQueryWrapper = new QueryWrapper<>();
        entityKillDropQueryWrapper.eq("entity_id", climbingTowerEntityInfoId);
        List<ClimbingTowerEntityKillDrop> entityKillDropList = climbingTowerEntityKillDropService.list(entityKillDropQueryWrapper);
        if (CollUtil.isNotEmpty(entityKillDropList)) {
            ClimbingTowerEntityKillDrop climbingTowerEntityKillDrop = entityKillDropList.get(RandomUtil.randomInt(entityKillDropList.size()));
            ClimbingTowerArticleInfo climbingTowerArticleInfo = climbingTowerArticleInfoService.getById(climbingTowerEntityKillDrop.getArticleId());
            msgList.add("<div style=\"color: blue\">获得物品：【" + climbingTowerArticleInfo.getName() + "】</div>");
            QueryWrapper<ClimbingTowerUserArticle> userArticleQueryWrapper = new QueryWrapper<>();
            userArticleQueryWrapper.eq("group_id", groupId);
            userArticleQueryWrapper.eq("user_id", userId);
            userArticleQueryWrapper.eq("article_id", climbingTowerArticleInfo.getId());
            ClimbingTowerUserArticle climbingTowerUserArticle = climbingTowerUserArticleService.getOne(userArticleQueryWrapper);
            if (climbingTowerUserArticle == null) {
                climbingTowerUserArticle = new ClimbingTowerUserArticle();
                climbingTowerUserArticle.setGroupId(groupId);
                climbingTowerUserArticle.setUserId(userId);
                climbingTowerUserArticle.setArticleId(climbingTowerArticleInfo.getId());
                climbingTowerUserArticle.setQuantity(1);
                climbingTowerUserArticleService.save(climbingTowerUserArticle);
            } else {
                climbingTowerUserArticle.setQuantity(climbingTowerUserArticle.getQuantity() + 1);
                climbingTowerUserArticleService.updateById(climbingTowerUserArticle);
            }
        }
    }

}
